package com.crane.oss.sys.service.impl;


import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.crane.common.core.dto.SessUser;
import com.crane.common.core.exception.AppException;
import com.crane.common.core.exception.BasicErrorCode;
import com.crane.common.core.exception.BizException;
import com.crane.common.framework.utils.BeanUtil;
import com.crane.oss.enums.MenuTypeEnum;
import com.crane.oss.enums.UserTypeEnum;
import com.crane.oss.properties.SysConfigProperties;
import com.crane.oss.sys.bean.request.menu.*;
import com.crane.oss.sys.bean.resp.ApiBean;
import com.crane.oss.sys.bean.resp.MenuBean;
import com.crane.oss.sys.bean.resp.MenuVueEleBean;
import com.crane.oss.sys.bean.resp.TreeMenuBean;
import com.crane.oss.sys.dao.MenuDao;
import com.crane.oss.sys.model.Menu;
import com.crane.oss.sys.service.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.*;

@Service("menuService")
public class MenuServiceImpl implements MenuService {

    private static final Logger logger = LoggerFactory.getLogger(MenuServiceImpl.class);

    @Resource
    protected MenuDao menuDao;

    @Resource
    protected MenuApiService menuApiService;

    @Resource
    protected MenuVueEleService menuVueEleService;

    @Resource
    protected RoleMenuService roleMenuService;

    @Resource
    protected ApiService apiService;

    @Resource
    protected SysConfigProperties sysConfigProperties;

    @Override
    public MenuBean queryMenu(Long menuId) {
        logger.info("MenuServiceImpl queryViewBean menuId={}", menuId);
        Menu menu = menuDao.selectById(menuId);
        if (null == menu) {
            throw new AppException(BasicErrorCode.ENTITY_NOT_EXISTS, "菜单");
        }
        MenuBean menuBean = new MenuBean();
        BeanUtil.copyProperties(menu, menuBean);

        if (menu.getType().equals(MenuTypeEnum.BUTTON.getValue())) {
            List<ApiBean> apiBeanList = apiService.queryApiListByMenuId(menuId);
            List<Long> apiIdList = new ArrayList<>();
            for (ApiBean apiBean : apiBeanList) {
                apiIdList.add(apiBean.getId());
            }
            menuBean.setApiIdList(apiIdList);
        }
        MenuVueEleBean menuVueEleBean = menuVueEleService.queryMenuVueEleByMenuId(menuId);
        if (null != menuVueEleBean) {
            menuBean.setTitle(menuVueEleBean.getMeta().get("title") == null ? "" : menuVueEleBean.getMeta()
                    .get("title").toString());
            menuBean.setComponent(menuVueEleBean.getComponent());
            menuBean.setRedirect(menuVueEleBean.getRedirect());
            menuBean.setHidden(menuVueEleBean.getHidden());
            menuBean.setName(menuVueEleBean.getName());
            menuBean.setUrl(menuVueEleBean.getPath());
        }
        return menuBean;
    }

    @Override
    public IPage<MenuBean> queryList(QueryMenuRequest queryRequest) {
        logger.info("MenuServiceImpl queryList queryRequest={}", JSON.toJSON(queryRequest));
        IPage<Menu> page = menuDao.selectPage(new Page<>(queryRequest.getPageNo(), queryRequest.getPageSize()),
                 Wrappers.lambdaQuery(Menu.class)
                .likeLeft(Menu::getName,queryRequest.getName())
                .eq(Menu::getParentId,queryRequest.getParentId()));

        return page.convert(u -> {
            MenuBean v = new MenuBean();
            BeanUtils.copyProperties(u, v);
            return v;
        });
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void save(AddMenuRequest addRequest) {
        logger.info("MenuServiceImpl save request={}", JSON.toJSON(addRequest));

        if (!addRequest.getType().equals(MenuTypeEnum.BUTTON.getValue())
                && !CollectionUtils.isEmpty(addRequest.getApiIdList())) {
            throw new BizException("接口不能为空");
        }
        Menu parentMenu = null;
        int level = 1;
        if (!addRequest.getParentId().equals(sysConfigProperties.getDefaultMenuId())) {
            parentMenu = menuDao.selectById(addRequest.getParentId());
            if (ObjectUtils.isEmpty(parentMenu)) {
                throw new AppException(BasicErrorCode.ENTITY_NOT_EXISTS, "父菜单");
            }
            level = parentMenu.getLevel() + 1;
        }
        Menu menu = new Menu();
        BeanUtil.copyProperties(addRequest, menu);
        menu.setLevel(level);
        menu.setName(addRequest.getTitle());
        menu.setCreateTime(new Date());
        menu.setCreateUserId(1L);
        menu.setUrl(addRequest.getUrl());
        menuDao.insert(menu);
        AddOrUpdateMenuVueEleRequest addOrUpdateMenuVueEleRequest = new AddOrUpdateMenuVueEleRequest();
        addOrUpdateMenuVueEleRequest.setMenuId(menu.getId());
        addOrUpdateMenuVueEleRequest.setComponent(addRequest.getComponent());
        addOrUpdateMenuVueEleRequest.setName(addRequest.getName());
        addOrUpdateMenuVueEleRequest.setPath(addRequest.getUrl());
        addOrUpdateMenuVueEleRequest.setRedirect(addRequest.getRedirect());
        addOrUpdateMenuVueEleRequest.setOrderNum(addRequest.getOrderNum());
        if (addRequest.getHidden()) {
            addOrUpdateMenuVueEleRequest.setHidden(1);
        } else {
            addOrUpdateMenuVueEleRequest.setHidden(0);
        }

        Map<String, Object> meta = new HashMap<>();
        meta.put("title", addRequest.getTitle());
        meta.put("icon", addRequest.getIcon());
        addOrUpdateMenuVueEleRequest.setMeta(JSON.toJSONString(meta));
        menuVueEleService.save(addOrUpdateMenuVueEleRequest);

        if (!CollectionUtils.isEmpty(addRequest.getApiIdList())) {
            menuApiService.saveBatch(menu.getId(), addRequest.getApiIdList());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(UpdateMenuRequest updateRequest) {
        logger.info("MenuServiceImpl update request={}", JSON.toJSON(updateRequest));
        Menu menu = menuDao.selectById(updateRequest.getId());
        if (null == menu) {
            throw new AppException(BasicErrorCode.ENTITY_NOT_EXISTS, "菜单");
        }

        Menu parentMenu = null;
        int level = 1;
        if (!updateRequest.getParentId().equals(-1)) {
            parentMenu = menuDao.selectById(updateRequest.getParentId());
            if (ObjectUtils.isEmpty(parentMenu)) {
                throw new AppException(BasicErrorCode.ENTITY_NOT_EXISTS, "父菜单");
            }
            level = parentMenu.getLevel() + 1;
        }
        BeanUtil.copyProperties(updateRequest, menu);
        menu.setLevel(level);
        menu.setName(updateRequest.getTitle());
        menu.setUpdateTime(new Date());
        menu.setUpdateUserId(1L);
        menu.setUrl(updateRequest.getUrl());
        menuDao.updateById(menu);

        AddOrUpdateMenuVueEleRequest addOrUpdateMenuVueEleRequest = new AddOrUpdateMenuVueEleRequest();
        addOrUpdateMenuVueEleRequest.setMenuId(menu.getId());
        addOrUpdateMenuVueEleRequest.setComponent(updateRequest.getComponent());
        addOrUpdateMenuVueEleRequest.setName(updateRequest.getName());
        addOrUpdateMenuVueEleRequest.setPath(updateRequest.getUrl());
        addOrUpdateMenuVueEleRequest.setRedirect(updateRequest.getRedirect());
        addOrUpdateMenuVueEleRequest.setOrderNum(updateRequest.getOrderNum());
        if (updateRequest.getHidden()) {
            addOrUpdateMenuVueEleRequest.setHidden(1);
        } else {
            addOrUpdateMenuVueEleRequest.setHidden(0);
        }
        Map<String, Object> meta = new HashMap<>();
        meta.put("title", updateRequest.getTitle());
        meta.put("icon", updateRequest.getIcon());
        addOrUpdateMenuVueEleRequest.setMeta(JSON.toJSONString(meta));
        menuVueEleService.update(addOrUpdateMenuVueEleRequest);

        List<Long> oldApiIdList = new ArrayList<>();
        if (menu.getType().equals(MenuTypeEnum.BUTTON.getValue())) {
            List<ApiBean> apiBeanList = apiService.queryApiListByMenuId(menu.getId());
            for (ApiBean apiBean : apiBeanList) {
                oldApiIdList.add(apiBean.getId());
            }
        }
        menuApiService.updateBatch(menu.getId(), oldApiIdList, updateRequest.getApiIdList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(Long menuId) {
        logger.info("MenuServiceImpl delete menuId={}", menuId);
        menuDao.deleteById(menuId);
        roleMenuService.deleteByMenuId(menuId);
        menuApiService.deleteByMenuId(menuId);
        menuVueEleService.deleteMenuVueEleByMenuId(menuId);
        _deleteChild(menuId);
    }

    @Override
    public List<MenuBean> queryRoleMenuList(Long roleId) {
        logger.info("MenuServiceImpl queryRoleMenuList roleId={}", roleId);
        List<Menu> menuList = menuDao.queryRoleMenuList(roleId);
        List<MenuBean> resultList = BeanUtil.copyList(menuList, MenuBean.class);
        return resultList;
    }

    @Override
    public List<MenuBean> queryRoleBtnMenuList(Long roleId) {
        logger.info("MenuServiceImpl queryRoleBtnMenuList roleId={}", roleId);
        List<Menu> menuList = menuDao.queryRoleBtnMenuList(roleId);
        List<MenuBean> resultList = BeanUtil.copyList(menuList, MenuBean.class);
        return resultList;
    }

    @Override
    public TreeMenuBean queryMenuTree(QueryMenuTreeRequest request, SessUser opUser) {
        logger.info("MenuServiceImpl queryChildrenList request={},opUser={}", request, opUser.getUserName());

        Long parentId = request.getParentId();
        if (parentId == null) {
            parentId = sysConfigProperties.getDefaultMenuId();
        }
        Menu parentMenu = menuDao.selectById(parentId);
        if (ObjectUtils.isEmpty(parentMenu)) {
            throw new AppException(BasicErrorCode.ENTITY_NOT_EXISTS, "父菜单");
        }
        List<Menu> menuList;
        if (opUser.getUserType().equals(UserTypeEnum.SUPER_ADMIN.getType())) {
            // 查询所有菜单信息
            menuList = menuDao.selectList(Wrappers.lambdaQuery(Menu.class).ne(!request.isHasButton(),Menu::getType,2));
        } else {
            // 只能查询用户某个角色的菜单信息
            menuList = menuDao.queryUserMenuList(opUser.getUserId(), request.getRoleId(), request.isHasButton());
        }
        TreeMenuBean treeMenuBean = new TreeMenuBean();
        _getMenuTreeList(parentMenu, menuList, treeMenuBean, request.isDisabled());
        return treeMenuBean;
    }

    private void _deleteChild(Long menuId) {
        List<Menu> menuls = menuDao.selectList(Wrappers.lambdaQuery(Menu.class).eq(Menu ::getParentId,menuId));
        if (!CollectionUtils.isEmpty(menuls)) {
            for (Menu temp : menuls) {
                Long delId = temp.getId();
                menuDao.deleteById(delId);
                roleMenuService.deleteByMenuId(menuId);
                menuApiService.deleteByMenuId(menuId);
                menuVueEleService.deleteMenuVueEleByMenuId(menuId);
                _deleteChild(delId);
            }
        }
    }

    private void _getMenuTreeList(Menu parentMenu, List<Menu> menuList, TreeMenuBean treeMenuBean, boolean disabled) {
        if (null != parentMenu) {
            _bulidTreeMenuBean(treeMenuBean, parentMenu, disabled);
        }
        for (Menu menu : menuList) {
            // 如果没有指定父节点,默认从 0 开始查找
            if (null == parentMenu) {
                if (menu.getParentId().equals(sysConfigProperties.getDefaultParentMenuId())) {
                    _getMenuTreeList(menu, menuList, treeMenuBean, disabled);
                }
            } else if (menu.getParentId().equals(parentMenu.getId())) {
                // 建立父子关系
                if (null == treeMenuBean.getChildren()) {
                    List<TreeMenuBean> children = new ArrayList<>();
                    treeMenuBean.setChildren(children);
                }
                TreeMenuBean childTreeMenuBean = new TreeMenuBean();
                treeMenuBean.getChildren().add(childTreeMenuBean);
                // 递归扫码子节点
                if (!menu.getType().equals(MenuTypeEnum.BUTTON.getValue())) {
                    _getMenuTreeList(menu, menuList, childTreeMenuBean, disabled);
                } else {
                    _bulidTreeMenuBean(childTreeMenuBean, menu, disabled);
                }
            }
        }
        // 对自己节点进行排序
        if (!CollectionUtils.isEmpty(treeMenuBean.getChildren())) {
            Collections.sort(treeMenuBean.getChildren());
        }
    }

    private void _bulidTreeMenuBean(TreeMenuBean treeMenuBean, Menu menu, boolean disabled) {
        treeMenuBean.setId(menu.getId());
        treeMenuBean.setLabel(menu.getName());
        treeMenuBean.setOrderNum(menu.getOrderNum());
        treeMenuBean.setDisabled(disabled);
        treeMenuBean.setIsLeaf(false);
        if (menu.getType().equals(MenuTypeEnum.BUTTON.getValue())) {
            treeMenuBean.setIsLeaf(true);
        }
    }

}
